﻿using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace CoreLims.Models.CoreLims
{
    using Result = SampleResult;

    public static class SampleResultExtension
    {
        /// <summary>
        /// 根据结果记录表ID，获得结果记录
        /// </summary>
        /// <param name="Result"></param>
        /// <param name="dbContext"></param>
        /// <param name="ResultId"></param>
        /// <returns></returns>
        public static SampleResult getResult(this SampleResult Result, CoreLimsContext dbContext, string ResultId) =>
            dbContext.Result.FirstOrDefault(s => s.Id == ResultId);

        public static List<SampleResult> getResultList(this SampleResult Result, CoreLimsContext dbContext) =>
            dbContext.Result.ToList();

        /// <summary>
        /// 根据 Sample 表的ID， 返回该样品对应的分析项列表
        /// </summary>
        /// <param name="Result"></param>
        /// <param name="dbContext"></param>
        /// <param name="SampleId"></param>
        /// <returns></returns>
        public static List<SampleResult> getResults(this SampleResult Result, CoreLimsContext dbContext, string SampleId) =>
            dbContext.Result.Where(s => s.SampleId == SampleId).ToList();

        /// <summary>
        /// 根据 Sample 表的ID， 返回该样品对应的分析项列表.
        /// 返回值为联合查询结果。
        /// </summary>
        /// <param name="sample"></param>
        /// <param name="dbContext"></param>
        /// <param name="Id"></param>
        /// <returns></returns>
        public static IQueryable<object> getSampleResults(this SampleResult Result, CoreLimsContext dbContext, string SampleId)
        {
            var list = from Sample in dbContext.Sample
                       join result in dbContext.Result on Sample.Id equals result.SampleId
                       where (Sample.Id == SampleId)
                       select new
                       {
                           result.Id,
                           Sample.SampleId,
                           Sample.SampleNumber,
                           result.TestName,
                           result.AnalytesCode,
                           result.AnalytesName,
                           result.LowA,
                           result.LowB,
                           result.StandardValue,
                           result.HighA,
                           result.HighB,
                           result.RoundingOff,
                           result.Units,
                           result.FUnits,
                           result.Original,
                           result.Final,
                           result.Conclusion,
                           result.RetestNo,
                           result.DetectionTimes,
                           result.TestTimes,
                           result.Status,
                           result.MethodId,
                           result.Assist,
                           result.Sort,
                           result.AlternativeResults,
                           result.EqUsageRecordId,
                           result.EqId,
                           result.EqType,
                           result.EntryModel,
                           result.EntryPersonName,
                           result.EntryTime,
                       };

            return list.OrderBy(s => s.TestName).ThenBy(s => s.TestTimes).ThenBy(s => s.AnalytesCode);
        }

        /// <summary>
        /// 使用该函数必须实例化对象。
        /// 该函数获得同一样品下、同一个测试内的所有分析项。
        /// </summary>
        /// <param name="Result"></param>
        /// <param name="dbContext"></param>
        /// <returns></returns>
        public static IQueryable<object> getSameTestResult(this SampleResult Result, CoreLimsContext dbContext)
        {
            var list = from Sample in dbContext.Sample
                       join result in dbContext.Result on Sample.Id equals result.SampleId
                       where (result.SampleId == Result.SampleId && result.TestId == Result.TestId && result.TestTimes == Result.TestTimes)
                       select new
                       {
                           result.Id,
                           Sample.SampleId,
                           result.TestName,
                           result.AnalytesCode,
                           result.AnalytesName,
                           result.LowA,
                           result.LowB,
                           result.HighA,
                           result.HighB,
                           result.RoundingOff,
                           result.Units,
                           result.Original,
                           result.Final,
                           result.Conclusion,
                           result.RetestNo,
                           result.DetectionTimes,
                           result.TestTimes,
                           result.Status,
                           result.MethodId,
                       };

            return list.OrderBy(s => s.AnalytesCode);
        }

        /// <summary>
        /// 手工录入结果处理
        /// </summary>
        /// <param name="Result"></param>
        /// <param name="dbContext"></param>
        /// <param name="Id"></param>
        /// <param name="Original"></param>
        /// <returns></returns>
        public static SampleResult ManualResult(this SampleResult Result, CoreLimsContext dbContext, string Id, string Original, string UserId, string UserName, out string except, string EqId = null)
        {
            var result = SAMPLE.Result.getResult(dbContext, Id);
            except = result.SetOriginal(dbContext, Original, SAMPLE.EntryModel.手工.ToString(), UserId, UserName);
            result.EqId = EqId;

            if (string.IsNullOrEmpty(except))
            {
                dbContext.Update(result);
                dbContext.SaveChanges();
            }
            return result;
        }

        /// <summary>
        /// 导入结果
        /// </summary>
        /// <param name="Result"></param>
        /// <param name="dbContext"></param>
        /// <param name="Id"></param>
        /// <param name="Original"></param>
        /// <param name="UserId"></param>
        /// <param name="UserName"></param>
        /// <param name="except"></param>
        /// <returns></returns>
        public static string ImportResult(this SampleResult Result, CoreLimsContext dbContext,string EqUsageRecordId, string EqID, List<ImportData.Result> RList, string UserId, string UserName)
        {
            if (RList.Count == 0)
                return "文件中没有找到需要导入的记录！";

            int scount = 0; // 导入样品总数
            int rcount = 0; // 导入结果个数
            foreach (var R in RList)
            {
                int a = 0;
                foreach (var code in R.AnalytesCode)
                {
                    var sample = SAMPLE.Sample.getSampleLoginBySampleId(dbContext, R.ID);
                    if (sample != null)
                    {
                        scount++;
                        var resultList = SAMPLE.Result.getResults(dbContext, sample.Id);
                        if (resultList != null)
                        {
                            var rl = resultList.Where(s => s.AnalytesCode == code && s.Final == null)
                                .OrderBy(s => s.TestTimes).OrderBy(s => s.DetectionTimes).FirstOrDefault();
                            if (rl != null)
                            {
                                rcount++;
                                rl.SetOriginal(dbContext, R.Value[a], SAMPLE.EntryModel.仪器.ToString(), UserId, UserName);
                                rl.EqUsageRecordId = EqUsageRecordId;
                                rl.EqId = EqID;
                                dbContext.Update(rl);
                                dbContext.SaveChanges();
                            }
                        }
                    }
                    a++;
                }
            }
            return EqUsageRecordId + ",本次共计导入样品数：" + scount.ToString() + " 结果总数：" + rcount.ToString();
        }

        /// <summary>
        /// 获得原始结果并进行后续处理
        /// </summary>
        /// <param name="Result"></param>
        /// <param name="dbContext"></param>
        /// <param name="value">原始结果</param>
        /// <param name="EntryModel">录入模式</param>
        /// <param name="except"></param>
        private static string SetOriginal(this SampleResult Result, CoreLimsContext dbContext, string value, string EntryModel, string UserId, string UserName)
        {
            if (Result.EntryPersonId != null && Result.EntryPersonId != UserId)
                return "用户：" + Result.EntryPersonName + " 已经处理该结果，您不能覆盖该值！";

            Result.Original = value;
            Result.EntryModel = EntryModel;
            Result.EntryTime = DateTime.Now;
            Result.Final = value;
            Result.EntryPersonId = UserId;
            Result.EntryPersonName = UserName;

            string except = null;
            Result.CallAcceptanceRules(dbContext, out except)
                .CallCalculationRules(dbContext, out except)
                .DetectionLimit()
                .RoundingOff()
                .CheckLowAndHigh()
                .CheckFinishResult(dbContext,except, UserId, UserName);

            return except;
        }

        /// <summary>
        /// 修约处理
        /// </summary>
        /// <param name="Result"></param>
        /// <returns></returns>
        private static SampleResult RoundingOff(this Result Result)
        {
            // 没定义修约或结果不能转换，不做修约处理
            decimal doriginal = 0;
            if (!string.IsNullOrEmpty(Result.RoundingOff) && decimal.TryParse(Result.Final, out doriginal))
            {
                var round = Result.RoundingOff.Split(',');
                if (round[0].Contains("ISO"))
                    Result.Final = Math.Round(doriginal, int.Parse(round[1]), MidpointRounding.AwayFromZero).ToString();

                if (round[0].Contains("FDA"))
                    Result.Final = Math.Round(doriginal, int.Parse(round[1])).ToString();

                if (round[0].Contains("ENOB"))
                    Result.Final = ReturnBef(doriginal, int.Parse(round[1])).ToString();
            }
            return Result;
        }

        /// <summary>
        /// 有效数字处理函数
        /// </summary>
        /// <param name="bef"></param>
        /// <param name="digit"></param>
        /// <returns></returns>
        private static string ReturnBef(decimal bef, int digit)
        {
            var res = bef.ToString($"G{digit}");
            if (res.Contains("."))
            {
                var index = Regex.Match(res, "([1-9])").Index;
                var sub = res.Substring(index).Replace(".", "");
                var leg = sub.Length;
                while (leg < digit)
                {
                    res += "0";
                    leg++;
                }
            }
            else
            {
                var leg = res.Length;
                res += ".";
                while (leg < digit)
                {
                    res += "0";
                    leg++;
                }
            }

            return res;
        }

        /// <summary>
        /// 检查 Low 和 High
        /// </summary>
        /// <param name="Result"></param>
        /// <returns></returns>
        private static SampleResult CheckLowAndHigh(this SampleResult Result)
        {
            decimal df = 0;
            Result.Status = SAMPLE.ResultStatus.Done.ToString();

            if (!decimal.TryParse(Result.Final, out df)) // 如果不能转换，则退出该函数
                return Result;

            decimal lowa = 0, higha = 0, lowb = 0, highb = 0;
            if (!string.IsNullOrEmpty(Result.LowA) && decimal.TryParse(Result.LowA, out lowa))
            {
                if (df < lowa)
                {
                    Result.Status = SAMPLE.ResultStatus.OOS.ToString();
                    return Result;
                }
            }

            if (!string.IsNullOrEmpty(Result.LowB) && decimal.TryParse(Result.LowB, out lowb))
            {
                if (df < lowb)
                {
                    Result.Status = SAMPLE.ResultStatus.OOG.ToString();
                }
            }

            if (!string.IsNullOrEmpty(Result.HighA) && decimal.TryParse(Result.HighA, out higha))
            {
                if (df > higha)
                {
                    Result.Status = SAMPLE.ResultStatus.OOG.ToString();
                }
            }

            if (!string.IsNullOrEmpty(Result.HighB) && decimal.TryParse(Result.HighB, out highb))
            {
                if (df > highb)
                {
                    Result.Status = SAMPLE.ResultStatus.OOS.ToString();
                    return Result;
                }
            }

            return Result;
        }

        /// <summary>
        /// 调用指标或指标集上的计算规则
        /// </summary>
        /// <param name="Result"></param>
        /// <param name="dbContext"></param>
        /// <param name="except">脚本异常时产生的错误信息</param>
        /// <returns></returns>
        private static SampleResult CallAcceptanceRules(this SampleResult Result, CoreLimsContext dbContext, out string except)
        {
            except = null;
            if (string.IsNullOrEmpty(Result.AcceptanceRules))
                return Result;

            var accpath = Common.getGlobalKeyValue(dbContext, "AcceptanceRules_Path");
            var fileName = accpath + "\\" + Result.AcceptanceRules + ".py";

            //var Original = new { Original = Result.Original };
            //var jResult = JsonConvert.SerializeObject(Result.Original);

            try
            {
                ScriptScope scope = getScope(dbContext, fileName);
                var _func = scope.GetVariable("AcceptanceRules");

                string results = _func(Result.Original);
                var arry = results.Split(',');
                if (arry.Count() == 3)
                {
                    if (arry[0] == "True")
                    {
                        Result.Final = arry[1];
                        Result.Conclusion = arry[2];
                    }
                }
                else
                {
                    except = results;
                    return Result;
                }
            }
            catch
            {
                except = "指标或指标集计算规则脚本调用出现异常！";
            }
            return Result;
        }

        private static ScriptScope getScope(CoreLimsContext dbContext, string fileName)
        {
            ScriptRuntime pyRuntime = Python.CreateRuntime(); //创建运行环境
            var pythonEngine = pyRuntime.GetEngine("python");

            var searchPaths = pythonEngine.GetSearchPaths();
            var path = dbContext.Global.Where(m => m.KeyName == "JSONStdlib_Path").FirstOrDefault().KeyValue;
            searchPaths.Add(path);

            pythonEngine.SetSearchPaths(searchPaths);
            pythonEngine.ImportModule("json");

            var script = pythonEngine.CreateScriptSourceFromFile(fileName);
            var code = script.Compile();
            var scope = pythonEngine.CreateScope();
            var excuteResult = code.Execute(scope);
            return scope;
        }

        /// <summary>
        /// 调用测试上定义的计算规则
        /// </summary>
        /// <param name="Result"></param>
        /// <param name="dbContext"></param>
        /// <param name="except">脚本异常时产生的错误信息</param>
        /// <returns></returns>
        private static SampleResult CallCalculationRules(this SampleResult Result, CoreLimsContext dbContext, out string except)
        {
            except = null;
            if (string.IsNullOrEmpty(Result.CalculationRules))
                return Result;

            var accpath = Common.getGlobalKeyValue(dbContext, "CalculationRules_Path");
            var fileName = accpath + "\\" + Result.CalculationRules + ".py";
            String builder = getJsonArrary(Result, dbContext);

            try
            {
                ScriptScope scope = getScope(dbContext, fileName);
                var _func = scope.GetVariable("CalculationRules");

                string results = _func(builder);
                var arry = results.Split(',');
                if (arry.Count() == 4)
                {
                    var result = dbContext.Result
                        .FirstOrDefault(s => s.AnalytesCode == arry[0] && s.DetectionTimes == Convert.ToInt32(arry[1]) && s.SampleId == Result.SampleId && s.TestId == Result.TestId && s.TestTimes == Result.TestTimes);
                    except = result.SetOriginal(dbContext, arry[1], SAMPLE.EntryModel.计算.ToString(),null, null);
                    Result.Conclusion = arry[2]; // 这是结论是自身的
                    dbContext.Update(result);
                }
                else
                {
                    except = results;
                    return Result;
                }
            }
            catch
            {
                except = "测试计算规则脚本调用出现异常！";
            }
            return Result;
        }

        /// <summary>
        /// 如果定义了检出限，则判断结果是否小于检出限，如果小于检出限，最终结果为‘小于检出限’
        /// </summary>
        /// <param name="Result"></param>
        /// <returns></returns>
        private static SampleResult DetectionLimit(this SampleResult Result)
        {
            if (Result.DetectionLimit != null) 
            {
                if (System.Convert.ToDecimal(Result.Final) <= System.Convert.ToDecimal(Result.DetectionLimit))
                {
                    Result.Final = "<" + Result.DetectionLimit;
                }
            }
            return Result;
        }

        /// <summary>
        /// 检查样品的结果值是否全部录入，如果全部录入则自动进入流程下一步
        /// </summary>
        /// <param name="Result"></param>
        /// <param name="dbContext"></param>
        /// <param name="except"></param>
        private static void CheckFinishResult(this SampleResult Result, CoreLimsContext dbContext, string except, string UserId = null, string UserName = null)
        {
            if (!string.IsNullOrEmpty(except))
                return;

            dbContext.SaveChanges(); // 保存以后再查询，结果才准确
            var count = dbContext.Result.Where(s => s.SampleId == Result.SampleId && string.IsNullOrEmpty(s.Final)).ToList().Count;
            if (count == 0)
            {
                var sample = dbContext.Sample.FirstOrDefault(s => s.Id == Result.SampleId);
                if (sample.CurrentWorkFlow == "结果录入") // 同级审核的时候依然可以修改结果，但是不能自动进入下一步
                    SAMPLE.RunWorkflow(dbContext, sample, UserId, UserName);
            }
        }

        /// <summary>
        /// 获得传递给测试脚本文件的字符串
        /// </summary>
        /// <param name="Result"></param>
        /// <param name="dbContext"></param>
        /// <returns></returns>
        private static String getJsonArrary(Result Result, CoreLimsContext dbContext)
        {
            var resultList = dbContext.Result
                .Where(s => s.SampleId == Result.SampleId && s.TestId == Result.TestId && s.TestTimes == Result.TestTimes)
                .ToList();

            StringBuilder builder = new StringBuilder("[");
            foreach (var result in resultList)
            {
                var item = new { AnalytesCode = result.AnalytesCode, DetectionTimes = result.DetectionTimes, Final = string.IsNullOrEmpty(result.Final)?"null":result.Final };
                var jitem = JsonConvert.SerializeObject(item);
                builder.Append(jitem + ",");
            }
            builder.Remove(builder.Length - 1, 1);
            builder.Append("]");
            return builder.ToString();
        }

        /// <summary>
        /// 生成需要重测的分析项数据
        /// </summary>
        /// <param name="Result"></param>
        /// <param name="dbContext"></param>
        /// <param name="ResultId"></param>
        public static void CreateReviewData(this SampleResult Result, CoreLimsContext dbContext, SampleResult result)
        {
            if (string.IsNullOrEmpty(result.TestId)) // 检查重测的分析项是否属于测试
            {
                var newResult = result.Copy();
                newResult.RetestNo = dbContext.Result
                    .Where(s => s.AnalytesId == newResult.AnalytesId && s.TestId == null && s.SampleId == newResult.SampleId).Count();
                newResult.DetectionTimes = 1;
                dbContext.Add(newResult);
            }
            else
            {
                var list = dbContext.Result.Where(s => s.TestId == result.TestId && s.TestTimes == result.TestTimes).ToList();
                var times = dbContext.Result.Where(s => s.TestId == result.TestId).Max(s => s.TestTimes) + 1;
                foreach (var item in list)
                {
                    var newResult = item.Copy();
                    newResult.TestTimes = times;
                    dbContext.Add(newResult);
                }
            }
        }

        public static string UpdateAssist(this SampleResult Result, CoreLimsContext dbContext, string ResultId, bool Assist)
        {
            var result = SAMPLE.Result.getResult(dbContext, ResultId);
            result.Assist = Assist;
            dbContext.Update(result);
            dbContext.SaveChanges();

            return "报告过滤更新成功！";
        }

        public static string UpdateResultFinalByFactor(this SampleResult Result, CoreLimsContext dbContext, string ResultId, string Factor, string Funits)
        {
            try
            {
                var result = SAMPLE.Result.getResult(dbContext, ResultId);
                result.FUnits = Funits;
                result.Final = (decimal.Parse(result.Original) * decimal.Parse(Factor)).ToString();
                dbContext.Update(result);
                dbContext.SaveChanges();
                return "结果转换成功！";
            }
            catch (Exception ex) { return ex.Message; }
        }

        /// <summary>
        /// 初始化指定分析项
        /// </summary>
        /// <param name="Result"></param>
        /// <param name="dbContext"></param>
        /// <param name="ResultId"></param>
        /// <returns></returns>
        public static string InitializeAnalysis(this SampleResult Result, CoreLimsContext dbContext, string ResultId)
        {
            var result = SAMPLE.Result.getResult(dbContext, ResultId);
            result.Original = null;
            result.Final = null;
            result.EqUsageRecordId = null;
            result.EqId = null;
            result.EntryModel = null;
            result.EntryPersonId = null;
            result.EntryPersonName = null;
            result.EntryTime = null;
            result.Status = null;
            result.Conclusion = null;
            dbContext.Update(result);
            dbContext.SaveChanges();

            return "初始化成功！";
        }

    }
}
